package main

import (
	"fmt"
	"reflect"
)

// main
//
//	@Description:
//
// 反射基本介绍~
// 反射可以在运行时动态获取变量的各种信息，比如变量的类型（type-细分类别），类别（kind-大类）
// 如果是结构体变量，还可以获取到结构体本身包含的字段（field），方法（method），
// 通过反射，可以修改变量的值，还可以调用关联的方法
// 使用反射 需要 import (“reflect”)
func main() {
	testReflectTypeAndValue()

	var a = 99
	reflectSetValue(&a)

	testStruct()
}

func testReflectTypeAndValue() {
	var a int = 10
	reflectTypeAndValue(a)
	var a1 float32 = 10.0
	reflectTypeAndValue(a1)
	var a2 string = "hello"
	reflectTypeAndValue(a2)

	var a3 Monster = Monster{
		Name: "牛魔王",
		Age:  500,
		Sal:  8000.0,
		Sex:  "男",
	}
	reflectTypeAndValue(a3)

	var slice []int = []int{1, 2, 3, 4, 5}
	reflectTypeAndValue(slice)

	var arr [5]int = [5]int{1, 2, 3, 4, 5}
	reflectTypeAndValue(arr)

	var monsters []Monster = []Monster{
		{
			Name: "牛魔王",
			Age:  500,
			Sal:  8000.0,
			Sex:  "男",
		},
		{
			Name: "牛魔王",
			Age:  500,
			Sal:  8000.0,
			Sex:  "男",
		},
	}
	reflectTypeAndValue(monsters)

	var map1 map[string]int = map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}
	reflectTypeAndValue(map1)
}

// reflectTypeAndValue
//
//	@Description: 获取类型和种类
//	@param x
func reflectTypeAndValue(x interface{}) {
	typeOf := reflect.TypeOf(x)
	fmt.Println("type=", typeOf)             //细分类型
	fmt.Println("type.Name=", typeOf.Name()) //数组、切片、map、指针等类型的变量，.Name都是返回为空
	kind := typeOf.Kind()
	fmt.Println("kind=", kind) //大类型
	valueOf := reflect.ValueOf(x)
	fmt.Printf("valueOf=%v  \n", valueOf)
	switch kind {
	case reflect.Int:
		nInt := int(valueOf.Int())
		fmt.Printf("nInt=%d \n", nInt)
	case reflect.Float32:
		nFloat := float32(valueOf.Float())
		fmt.Printf("nFloat=%f \n", nFloat)
	case reflect.String:
		nString := valueOf.String()
		fmt.Printf("nString=%s \n", nString)
	case reflect.Array:
		for i := 0; i < valueOf.Len(); i++ {
			fmt.Printf("index=%d value=%v \n", i, valueOf.Index(i).Interface())
		}
	case reflect.Slice:
		for i := 0; i < valueOf.Len(); i++ {
			fmt.Printf("index=%d value=%v \n", i, valueOf.Index(i).Interface())
		}
	default:
		fmt.Println("其他类型")

	}
	fmt.Println()
}

// reflectSetValue
//
//	@Description: 设置值
//	@param x
func reflectSetValue(x interface{}) {
	valueOf := reflect.ValueOf(x)
	kind := valueOf.Kind()
	if kind == reflect.Ptr {
		switch valueOf.Elem().Kind() {
		case reflect.Int:
			valueOf.Elem().SetInt(100)
		case reflect.Float32:
			valueOf.Elem().SetFloat(100.0)
		}
		fmt.Println(valueOf.Elem().Interface())
	} else {
		panic("传入参数错误")
	}

}

type Monster struct {
	Name string  `json:"monsterName" ini:"s_name"`
	Age  int     `json:"monsterAge" ini:"s_age"`
	Sal  float64 `json:"monsterSal"  ini:"s_sal"`
	Sex  string  `json:"monsterSex"  ini:"s_sex"`
}

func (m Monster) Sum(a, b int) int {
	var c = a + b
	return c
}

func testStruct() {
	var monster Monster = Monster{
		Name: "牛魔王",
		Age:  500,
		Sal:  8000.0,
		Sex:  "男",
	}
	valueOf := reflect.ValueOf(monster)
	typeOf := reflect.TypeOf(monster)

	//获取字段数量
	numField := typeOf.NumField()
	fmt.Println("numField=", numField)
	for i := 0; i < numField; i++ {
		//根据index获取字段的StructField
		structField := typeOf.Field(i)
		fmt.Printf("Name=%v Type=%v Tag=%v ini=%v \n", structField.Name, structField.Type, structField.Tag, structField.Tag.Get("ini"))
	}
	//根据字段名获取字段的StructField
	nameStructField, ok := typeOf.FieldByName("Name")
	if ok {
		fmt.Printf("Name=%v Type=%v Tag=%v ini=%v \n", nameStructField.Name, nameStructField.Type, nameStructField.Tag, nameStructField.Tag.Get("ini"))
	} else {
		fmt.Println("找不到字段")
	}
	//获取方法数量
	methodNum := typeOf.NumMethod()
	fmt.Println("methodNum=", methodNum)
	for i := 0; i < methodNum; i++ {
		//根据index获取方法
		method := typeOf.Method(i)
		fmt.Printf("method.Name=%v method.Type=%v \n", method.Name, method.Type)

		//获取方法参数类型
		for i := 0; i < method.Type.NumIn(); i++ {
			in := method.Type.In(i)
			fmt.Printf("参数index=%v 参数类型：%v \n", i, in) //第0个参数是接收器
		}

		//获取方法返回值类型
		for i := 0; i < method.Type.NumOut(); i++ {
			out := method.Type.Out(i)
			fmt.Printf("返回值index=%v 返回值类型：%v \n", i, out)
		}

		//方法调用一
		fmt.Println("方法调用一：")
		funC := method.Func
		var res = funC.Call([]reflect.Value{valueOf, reflect.ValueOf(10), reflect.ValueOf(40)})
		fmt.Printf("res=%v \n", res[0].Int())

		fmt.Println("方法调用二：")
		funC1 := valueOf.Method(i)
		var params01 = make([]reflect.Value, 0)
		params01 = append(params01, reflect.ValueOf(10))
		params01 = append(params01, reflect.ValueOf(40))
		var res1 = funC1.Call(params01)
		fmt.Printf("res=%v \n", res1[0].Int())
	}

	//根据方法名字获取方法
	m, isOk := typeOf.MethodByName("Sum")
	if isOk {
		fmt.Printf("m.Name=%v m.Type=%v \n", m.Name, m.Type)
	}

}
